#include <QMessageBox>
#include <QNetworkInterface>
#include "Change/message.h"
#include "Networking/usermanager.h"
#include "MainWindow/mainwindow.h"
#include "constants.h"
#include "server.h"

Server::Server(QString newName, int newPort, Session *currSession)
{
	name = newName;
	port = newPort;
	session = currSession;
	bonjour = NULL;

	//set up a connection to mainWindow, so we can send error and alert messages to the GUI thread
	connect(this, SIGNAL(showMessageBox(int,QString)), MainWindow::getInstance(), SLOT(showMessageBox(int,QString)));
}

Server::~Server()
{
	if (sock)
		delete sock;
}

void Server::run()
{
	//start the server
	sock = new QTcpServer(this);
	connect(sock, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
	sock->listen(QHostAddress::Any, (quint16)port);

	//register the service with bonjour
	bonjour = new BonjourRegister(this);
	connect(bonjour, SIGNAL(bonjourError()), this, SLOT(handleBonjourError()));
	bonjour->registerService(name, QString(BONJOUR_PROTOCOL), (quint16)port);

	exec();
}

/**
  * Displays a nice error message if the bonjour registration fails.
  * Tells the user instructions on how to connect manually instead
  */
void Server::handleBonjourError()
{
	//figure out the host ip (or guess a nice-ish one)
	QString host = "127.0.0.1";
	QList<QHostAddress> interfaceList = QNetworkInterface::allAddresses();

	for (int i = 1; i < interfaceList.length(); i++)
	{
		if (interfaceList.at(i).protocol() == QAbstractSocket::IPv4Protocol)
		{
			host = interfaceList.at(i).toString();
			break;
		}
	}

	QString msg = "The session could not be registered with Bonjour.\n\nThis means that users will have to";
	msg += " connect manually using the following infomation:\n\nIP Address: " + host + "\nPort: " + QString::number(port);

	emit showMessageBox(QMessageBox::Warning, msg);
}

/**
  *	Handles a new connection, by performing the handshake procedure/s
  * and then adding the connection to the list of connections
  */
void Server::handleNewConnection()
{
	qDebug("WeaveServer has received a connection");

	while (sock->hasPendingConnections())
	{
		QTcpSocket *conn = sock->nextPendingConnection();

		if (!connections.empty())	//if the server is connecting to itself, no need to handshake
		{
			//write the name of the session to initiate
			QByteArray bytes;
			QDataStream sout(&bytes, QIODevice::WriteOnly);
			//sout << (quint8)Message::SESSION_NAME << name << endl;
			sout << name << "\n";

			conn->write(packageMessage(bytes));

			//write the list of users to the client
			foreach(User usr, session->getUserManager()->getUserList())
			{
				qDebug("WeaveServer sends user data (%d, %s) to client", usr.getId(), usr.getName().toAscii().data());
				conn->write(packageMessage(UserMessage(usr).toBytes()));
			}

			//Send out currently opened documents
			if(session->getDocumentManager()->getDocList().size()>0)    //There are documents
			{
			    QByteArray myBytes;
			    session->saveSession(myBytes);	//Get all data

			    quint8 op = Message::INITDOC;
			    myBytes.prepend(op);

			    conn->write(packageMessage(myBytes));
			}
		}

		//accept the connection
		connect(conn, SIGNAL(readyRead()), this, SLOT(receiveMessage()));
		connections.append(conn);
	}
}



/**
  * Prepends the size of the message to the message, for use when sending messages
  */
QByteArray Server::packageMessage(QByteArray bytes)
{
	QByteArray sizeArray;

	QDataStream sout(&sizeArray, QIODevice::WriteOnly);
	sout << (qint64)bytes.size();

	bytes.prepend(sizeArray);

	return bytes;
}

/**
  *	Sends the given data to all connected clients
  */
void Server::sendToAll(QByteArray msg)
{
	foreach (QTcpSocket *socket, connections)
	{
		socket->write(msg);
	}
}

/**
  *	Recieve a message and process it, then send it back out to everyone
  */
void Server::receiveMessage()
{
	foreach (QTcpSocket *socket, connections)
	{
		if (socket->bytesAvailable())
		{
			QByteArray rawMsg = socket->readAll();

			processMessage(rawMsg);
		}
	}
}


void Server::processMessage(QByteArray msg)
{
	sendToAll(msg);
}

